import {useEffect, useMemo, useRef, useState} from "react";
import {Button, Spin, TabPane, Tabs, Toast} from "@douyinfe/semi-ui";

import {bitable, FieldType, IFieldMeta as FieldMeta, IRecord} from "@lark-base-open/js-sdk";
import {useTranslation} from "react-i18next";

import EncryptForm from "../components/encrypt-form";
import DecryptForm from "../components/decrypt-form";
import {decryptCbc, desensitized, encryptCbc} from "../util/encrypt";

//@ts-ignore
window.bitable = bitable;

/** 表格，字段变化的时候刷新插件 */
export default function Encrypt() {
  const [key, setKey] = useState<string | number>(0);
  const [tableList, setTableList] = useState([]);
  const currentTableId = useRef<string|null>();
  // 绑定过的tableId
  const bindList = useRef<Set<string>>(new Set());

  const refresh = useMemo(
    () => () => {
      const t = new Date().getTime();
      setKey(t);
    },
    []
  );

  useEffect(() => {
    bitable.base.getTableList().then((list) => {
      setTableList(list);
    });
    const deleteOff = bitable.base.onTableDelete(() => {
      setKey(new Date().getTime());
    });
    const addOff = bitable.base.onTableAdd(() => {
      setKey(new Date().getTime());
      bitable.base.getTableList().then((list) => {
        setTableList(list);
      });
    });
    const selectionOff = bitable.base.onSelectionChange((selection)=>{
      const tableId = selection.data.tableId;
      if(tableId !== currentTableId.current) {
        currentTableId.current  = tableId
        setKey(new Date().getTime());
      }
    });
    return () => {
      deleteOff();
      addOff();
      selectionOff();
    };
  }, []);

  useEffect(() => {
    if (tableList.length) {
      tableList.forEach((table) => {
        if (bindList.current.has(table.id)) {
          return;
        }
        table.onFieldAdd(refresh);
        table.onFieldDelete(refresh);
        table.onFieldModify(refresh);
        bindList.current.add(table.id);
      });
    }
  }, [tableList]);

  return <TabList key={key} />;
}

const TabList = ()=> {
  const [btnDisabled, setBtnDisabled] = useState(true);
  const [tableLoading, setTableLoading] = useState(true);
  const [textTypeFields, setTextTypeFields] = useState<FieldMeta[]>([]);
  const [buttonLoading, setButtonLoading] = useState(false);


  const [activeTab, setActiveTab] = useState<string>('1');
  const [activeTableId, setActiveTableId] = useState<string>();
  const [activeTableName, setActiveTableName] = useState<string>();

  const [encryptType, setEncryptType] = useState();
  const [encryptScope, setEncryptScope] = useState();
  const [encryptPassword, setEncryptPassword] = useState('');
  const [encryptStartRow, setEncryptStartRow] = useState();
  const [encryptSourceFieldId, setEncryptSourceFieldId] = useState();
  const [desensitizeType, setDesensitizeType] = useState();

  const [decryptType, setDecryptType] = useState();
  const [decryptScope, setDecryptScope] = useState();
  const [decryptPassword, setDecryptPassword] = useState('');
  const [decryptStartRow, setDecryptStartRow] = useState();
  const [decryptSourceFieldId, setDecryptSourceFieldId] = useState();


  const {t} = useTranslation();

  const init = async ()=> {
    setTableLoading(true);
    const tableList = await bitable.base.getTableMetaList();
    const selection = await bitable.base.getSelection();
    const activeTableId = selection?.tableId;
    if(activeTableId) {
      setActiveTableId(activeTableId);
      setActiveTableName(tableList.find((table) => table.id===activeTableId)?.name);
      setTableLoading(false);
    }
  }

  const switchTable = async (activeTableId: string)=> {
    setActiveTableId(activeTableId);
    setTableLoading(false);
    const tableInfo = await bitable.base.getTableById(activeTableId);
    const activeTableName = await tableInfo.getName();
    setActiveTableName(activeTableName);
    const filedMetaList = await tableInfo.getFieldMetaList();
    const textTypeFields: FieldMeta[] = [];
    for (const item of filedMetaList) {
      switch(item.type) {
        case FieldType.Text:
          textTypeFields.push(item);
          break;
        case FieldType.Formula:
          const field = await tableInfo.getFieldById(item.id);
          const proxyType = await field.getProxyType();
          if (proxyType === FieldType.Text) {
            textTypeFields.push(item);
          }
          break;
        default:
          break;
      }
    }
    setTextTypeFields(textTypeFields);
  }

  const commonCheck = (sourceFieldId, type, scope, startRow, password, desensitizeType)=> {
    const result = {
      checkResult: true,
      errorMessage: ""
    }
    if(!sourceFieldId) {
      result.checkResult = false;
      result.errorMessage = t("source.field.empty");
      return result;
    }
    if(scope === 'part' && !startRow) {
      result.checkResult = false;
      result.errorMessage = t("start.row.empty");
      return result;
    }
    if(type === 'symmetric' && !password) {
      result.checkResult = false;
      result.errorMessage = t("password.field.empty");
      return result;
    }
    if (activeTab === '1' && type === 'desensitization' && !desensitizeType) {
      result.checkResult = false;
      result.errorMessage = t("desensitize.type.empty");
      return result;
    }
    return result;
  }

  const getRasKey = async ()=> {
    const fetchResult = await fetch(window.location.origin + "/api/rsa");
    if (fetchResult.status === 200) {
      const result = await fetchResult.json();
      return result;
    }
  }

  const processData = async (waitProcessList, type, password) => {
    if (activeTab === '1') {
      if (type === 'desensitization') {
        return waitProcessList?.map(item => {
          return { recordId: item.recordId, value: desensitized(item.value, desensitizeType)}
        });
      } else if (type === 'symmetric') {
        return waitProcessList?.map(item => {
          return { recordId: item.recordId, value: encryptCbc(password, item.value)}
        });
      } else if (type === 'asymmetric') {
        const userId = await bitable.bridge.getUserId();
        let data = await bitable.bridge.getData(userId + ":rsa:key");
        if(!data?.publicKey) {
          const rsa = await getRasKey();
          if (!rsa) {
            Toast.error(t("encrypt.error"));
            return waitProcessList;
          }
          data = {publicKey: rsa.publicKey, privateKey: rsa.privateKey};
          await bitable.bridge.setData(userId + ":rsa:key", {publicKey:data.publicKey, privateKey: data.privateKey});
        }
        const encryptFetchResult = await fetch(window.location.origin + "/api/rsa-encrypt", {
          headers: {
            "Content-Type": "application/json"
          },
          method: 'POST',
          body: JSON.stringify({publicKey: data.publicKey, data: waitProcessList})
        });
        if (encryptFetchResult.status !== 200) {
          Toast.error(t("encrypt.error"));
        } else {
          const res = await encryptFetchResult.json();
          return res?.encryptArray || waitProcessList;
        }
      }
    } else if (activeTab === '2') {
      if (type === 'symmetric') {
        return waitProcessList?.map(item => {
          return { recordId: item.recordId, value: decryptCbc(password, item.value) || item.value}
        });
      } else if (type === 'asymmetric') {
        const userId = await bitable.bridge.getUserId();
        let data = await bitable.bridge.getData(userId + ":rsa:key");
        if(!data?.privateKey) {
          Toast.error(t("decrypt.error"));
        } else {
          const decryptFetchResult = await fetch(window.location.origin + "/api/rsa-decrypt", {
            headers: {
              "Content-Type": "application/json"
            },
            method: 'POST',
            body: JSON.stringify({privateKey: data.privateKey, data: waitProcessList})
          });
          if (decryptFetchResult.status !== 200) {
            Toast.error(t("encrypt.error"));
          } else {
            const res = await decryptFetchResult.json();
            return res?.decryptArray || waitProcessList;
          }
        }
      }
    }
    return waitProcessList;
  }

  const buttonClick = async () => {
    let sourceFieldId, type, scope, startRow, password;
    if(activeTab === '1') {
      sourceFieldId = encryptSourceFieldId;
      type = encryptType;
      scope = encryptScope;
      startRow = encryptStartRow;
      password = encryptPassword;
    } else if(activeTab === '2') {
      sourceFieldId = decryptSourceFieldId;
      type = decryptType;
      scope = decryptScope;
      startRow = decryptStartRow;
      password = decryptPassword;
    }
    const result = commonCheck(sourceFieldId, type, scope, startRow, password, desensitizeType);
    if (!result.checkResult) {
      Toast.error(result.errorMessage);
    }
    setButtonLoading(true);
    const table = await bitable.base.getTableById(activeTableId);
    const sourceField = await table.getFieldById(sourceFieldId);
    const sourceValueList = await sourceField.getFieldValueList();



    // 每次提取100条记录
    for (let i = 0; i < sourceValueList.length; i += 100) {
      const waitProcessList: any[] = [];
      let subList = sourceValueList.slice(i, i + 100);
      subList.forEach(({ record_id, value }, index) => {
        if(scope === 'part' && startRow && index+1 < startRow) {
          return;
        }
        if (Array.isArray(value)) {
          waitProcessList.push({
            recordId: record_id,
            value: value?.map(({ type, text }: any) => text).join(""),
          });
        }
      });
      let processedArray = await processData(waitProcessList, type, password);
      if (Array.isArray(processedArray)) {
        const records: Array<IRecord> = [];
        processedArray?.forEach(({ recordId, value }: any) =>
          records.push({
            recordId: recordId,
            fields: {
              [sourceFieldId]: value,
            }
          })
        );
        await table.setRecords(records);
      }
    }
    setButtonLoading(false);
    Toast.success(t("nlp.execute.success"));
  };


  useEffect(() => {
    init();
  }, []);

  useEffect(() => {
    if (!activeTableId) {
      return;
    }
    switchTable(activeTableId)
  }, [activeTableId]);

  useEffect(() => {
    if(activeTab === '1') {
      const result = commonCheck(encryptSourceFieldId, encryptType, encryptScope, encryptStartRow, encryptPassword, desensitizeType);
      setBtnDisabled(!result.checkResult)
    } else if(activeTab === '2') {
      const result = commonCheck(decryptSourceFieldId, decryptType, decryptScope, decryptStartRow, decryptPassword, null);
      setBtnDisabled(!result.checkResult)
    }
  }, [activeTab, encryptType, encryptScope, encryptPassword, encryptStartRow, encryptSourceFieldId, decryptType, decryptScope, decryptPassword, decryptStartRow, decryptSourceFieldId, desensitizeType]);


  return (
    <div>
      <Spin spinning={tableLoading}>
        <div style={{padding:10}}>{`${t('encrypt.current.table')}: ${activeTableName||''}`}</div>

        <Tabs style={{padding: 10}} type="line" onChange={setActiveTab}>
          <TabPane tab={t('encrypt.encrypt.tab')} itemKey="1">
            <EncryptForm
              textTypeFields={textTypeFields}
              getSourceFiledId={setEncryptSourceFieldId}
              getStartRow={setEncryptStartRow}
              getPassword={setEncryptPassword}
              getDecryptType={setEncryptType}
              getDecryptScope={setEncryptScope}
              getDesensitizeType={setDesensitizeType}
            />
          </TabPane>
          <TabPane tab={t('encrypt.decrypt.tab')} itemKey="2">
            <DecryptForm
              textTypeFields={textTypeFields}
              getSourceFiledId={setDecryptSourceFieldId}
              getStartRow={setDecryptStartRow}
              getPassword={setDecryptPassword}
              getDecryptType={setDecryptType}
              getDecryptScope={setDecryptScope}
            />
          </TabPane>
        </Tabs>


      </Spin>{" "}
      <Button
        disabled={btnDisabled}
        type="primary"
        className="bt1"
        loading={buttonLoading}
        style={{margin:"0 10px"}}
        onClick={buttonClick}
      >
        {`${activeTab==='1'?t("encrypt.execute.start"):t("decrypt.execute.start")}`}
      </Button>
    </div>
  );
}
